package tld.test.menu.challenges
{
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.IEventDispatcher;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.utils.Dictionary;
	
	import tld.test.interpreter.IContext;
	import tld.test.interpreter.runtime.Runtime;
	import tld.test.menu.Challenge;
	import tld.test.menu.Console;
	import tld.test.menu.IChallenge;
	
	public class ChallengeTheChallenge extends Sprite implements IChallenge
	{
		private const _challenge:Challenge = new Challenge();
		
		private const _names:Object = 
		{ 
			"load-challenge" : loadChallenge, 
			"unload" : unload
		};
		
		private const _challenges:Object = 
		{ 
			"quiz" : Quiz,
			"video-player" : VideoPlayer,
			"ashes-to-ashes" : AshesToAshes
		}
		
		private const _quizButton:TextField = new TextField();
		
		private const _videoButton:TextField = new TextField();
		
		private const _ashesButton:TextField = new TextField();
		
		private const _unloadButton:TextField = new TextField();
		
		private const _format:TextFormat = new TextFormat("_sans", 12);
		
		private const _buttons:Dictionary = new Dictionary();
		
		private var _loaded:IChallenge;
		
		private var _runtime:Runtime;
		
		private var _console:Console;
		
		public function ChallengeTheChallenge()
		{
			super();
			for (var name:String in this._names)
				this._challenge.put(name, this._names[name]);
			this.draw();
		}
		
		public function setRuntime(runtime:Runtime):void
		{
			this._runtime = runtime;
			this._runtime.print(this.help());
			this._runtime.addContext(this._challenge);
		}
		
		public function setConsole(console:Console):void
		{
			this._console = console;
		}
		
		public function challenge():IContext
		{
			return this._challenge;
		}
		
		public function help():String
		{
			return <![CDATA[;;; This is ChallengeTheChallenge program

; To use this program, type into console, or interact using 
; your pointing device.

; The program understand a rather small subset of Lisp-like
; language.
; Operations currently suported:
;   Math: + - * / % >> >>> << & | floor ceil round expt sqrt
;     for example
;     (print
;      (round (/ (| 7 (* (<< 1 1) 3))
;       (/ 1 (min (& (- (expt 2 3) 1) 6) 100.234)))))
;     will print `42'
;   Logic: && || ! = 
;     The language also supports `if' special form, for example
;     (if (= 2 3) (print "two equals three") (print "nonsense!"))
;     will output `nonsenese!'
;   Utilities:
;     print [arg] [...] - prints arguments to the console (or to the log)
;     load <url> - loads script from `url'
; you can add modules which extend this functionality.

; This program loads other challenges to challenge you on
; differnt skillsets.
; call (unload) to unload the last loaded challenge.
; call (load-challenge <challenge name>) to load up a challenge.
;       Once loaded the new challenge will replace the old one.
;       Available challenges are: "ashes-to-ashes", 
;       "quiz" and "video-player".]]>.toString();
		}
		
		public function loadChallenge(which:String):void
		{
			var newChallenge:IChallenge;
			if (this._loaded) this.unload();
			while (super.numChildren) super.removeChildAt(0);
			if (this._challenges[which] is Class)
			{
				newChallenge = new this._challenges[which]();
				this._challenges[which] = newChallenge;
			}
			else if (which in this._challenges)
				newChallenge = this._challenges[which];
			else this._runtime.print("; Error: no such challenge");
			if (newChallenge)
			{
				super.addChild(newChallenge as DisplayObject);
				this._runtime.print(newChallenge.help());
				newChallenge.challenge().setParent(null);
				(newChallenge as IEventDispatcher).addEventListener(
					ChallengeEvent.LOOSE, this.looseHandler);
				(newChallenge as IEventDispatcher).addEventListener(
					ChallengeEvent.WIN, this.winHandler);
				this._runtime.addContext(newChallenge.challenge());
				newChallenge.setConsole(this._console);
				this._console.run(newChallenge.defaults());
				this._console.submit();
				this._loaded = newChallenge;
			}
		}
		
		public function defaults():String
		{
			return "";
		}
		
		public function unload():void
		{
			if (this._loaded)
			{
				super.removeChild(this._loaded as DisplayObject);
				this._runtime.removeContext(this._loaded.challenge());
				this._runtime.print("; Challenge unloaded");
				(this._loaded as IEventDispatcher).removeEventListener(
					ChallengeEvent.LOOSE, this.looseHandler);
				(this._loaded as IEventDispatcher).removeEventListener(
					ChallengeEvent.WIN, this.winHandler);
				this._loaded = null;
				this.draw();
			}
		}
		
		private function draw():void
		{
			this.drawButton(this._quizButton, "quiz");
			this.drawButton(this._videoButton, "video-player");
			this.drawButton(this._ashesButton, "ashes-to-ashes");
			this.drawButton(this._unloadButton, "unload");
		}
		
		private function looseHandler(event:ChallengeEvent):void
		{
			this._console.print("; You loose!");
		}
		
		private function winHandler(event:ChallengeEvent):void
		{
			this._console.print("; You win!");
		}
		
		private function drawButton(button:TextField, label:String):TextField
		{
			button.defaultTextFormat = this._format;
			button.autoSize = TextFieldAutoSize.LEFT;
			button.background = true;
			button.backgroundColor = 0xAAAAAA; // RGH!
			button.text = label;
			button.selectable = false;
			this._buttons[button] = label;
			button.addEventListener(MouseEvent.CLICK, this.clickHandler);
			button.y = super.height;
			return super.addChild(button) as TextField;
		}
		
		private function clickHandler(event:MouseEvent):void
		{
			var tag:String = this._buttons[event.currentTarget]; 
			if (tag in this._names)
				this._console.run("(" + tag + ")");
			else if (tag in this._challenges)
				this._console.run("(load-challenge \"" + tag + "\")");
		}
	}
}